#pragma once

#include "Sock.hpp"
#include "Epoll.hpp"

#include <cassert>
#include <functional>

using func_t = std::function<void(std::string)>;

class EpollServer {
#define MAX_EVENTS 100
public:
    EpollServer(func_t process, uint16_t port = 8080)
      :_port(port)
      ,_process(process)
  {
    _listensock = Socket::SockInit();

    Socket::SockBind(_listensock, _port);
    Socket::SockListen(_listensock);

    _epfd = Epoll::CreateEpoll();

    Epoll::CtlEpoll(_epfd, EPOLL_CTL_ADD, _listensock, EPOLLIN);
    
    _revents = new epoll_event[MAX_EVENTS];
    _maxRevents = MAX_EVENTS;
  }

    void Start() {
      int timeout = -1;
      while(1) {
        int res = Epoll::WaitEpoll(_epfd, _revents, _maxRevents, timeout);
        if(res == 0) {
          std::cout << "time out" << std::endl;
        } else {
          Handler(res);
        }

      }
    }

    ~EpollServer() {
      
      if(_listensock >= 0) {
        close(_listensock);
      }

      if(_revents != nullptr) {
        delete  []_revents;
      }

      if(_epfd >= 0) {
        close(_epfd);
      }

    }
private:
    void Handler(int n) {
      for(int i = 0; i < n; i++) {
        uint32_t event = _revents[i].events;
        int sock = _revents[i].data.fd;

        if(event & EPOLLIN) {
          if(sock == _listensock) {
            Accepter();
          } else {
            Recver(sock);
          }
        }
      }
    }
    
    void Accepter() {
      std::string clientIP;
      uint16_t clientPort;
      int sock = Socket::SockAccept(_listensock, clientIP, clientPort);
      if(sock > 0) {
        if(Epoll::CtlEpoll(_epfd, EPOLL_CTL_ADD, sock, EPOLLIN)) {
          std::cout << sock << "in poll" << std::endl;
        }
      }
    }

    void Recver(int sock) {
      char buff[128];
      int res = read(sock, buff, sizeof(buff) - 1);

      if(res > 0) {
        buff[res] = 0;
        _process(buff);
      } else if(res == 0) {
        printf("client[%d] close, me too\n", sock);
        bool res = Epoll::CtlEpoll(_epfd, EPOLL_CTL_DEL, sock, EPOLLIN);
        assert(res);
        (void)res;
        close(sock);
      } else {
        printf("client[%d] read err, errno[%d]::%s\n", sock, errno, strerror(errno));
        bool res = Epoll::CtlEpoll(_epfd, EPOLL_CTL_DEL, sock, EPOLLIN);
        assert(res);
        (void)res;
        close(sock);
      }
    }

    int _listensock;
    uint16_t _port;
    int _epfd;
    epoll_event* _revents;
    int _maxRevents;
    func_t _process;
};
